package com.syf.core.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.TypeUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class BeanPathUtil {
    public static List<String> getBeanPathList(Class<?> clazz) {
        List<String> beanPaths = new ArrayList<>();

        scanBeanPath(clazz, new StringBuilder(), beanPaths);

        return beanPaths;

    }

    private static void scanBeanPath(Type type, StringBuilder startPath, List<String> beanPaths) {
        Class<?> clazz = TypeUtil.getClass(type);
        if (BeanUtil.isBean(clazz)) {
            Field[] declaredFields = ClassUtil.getDeclaredFields(clazz);
            for (Field declaredField : declaredFields) {
                // 去掉静态field
                if (Modifier.isStatic(declaredField.getModifiers())) {
                    continue;
                }

                StringBuilder newStart = new StringBuilder(startPath);
                newStart.append(".").append(declaredField.getName());
                scanBeanPath(declaredField.getGenericType(), newStart, beanPaths);
            }
            return;
        }

        if (StrUtil.isBlank(startPath)) {
            // 第一次进来必须是一个bean
            return;
        }

        if (String.class.isAssignableFrom(clazz) || ClassUtil.isBasicType(clazz)) {
            beanPaths.add(startPath.substring(1));
            return;
        }

        if (clazz.isArray()) {
            startPath.append("[*]");

            Class<?> subClazz = clazz.getComponentType();
            if (String.class.isAssignableFrom(subClazz) || ClassUtil.isBasicType(subClazz)) {
                beanPaths.add(startPath.substring(1));
                return;
            }

            scanBeanPath(subClazz, startPath, beanPaths);
            return;
        }

        if (List.class.isAssignableFrom(clazz)) {
            startPath.append("[*]");
            Class<?> subClazz = TypeUtil.getClass(getActualTypeOfList(type));
            if (subClazz == null || String.class.isAssignableFrom(subClazz) || ClassUtil.isBasicType(subClazz)) {
                beanPaths.add(startPath.substring(1));
                return;
            }

            scanBeanPath(subClazz, startPath, beanPaths);
            return;
        }

        if (Map.class.isAssignableFrom(clazz)) {
            startPath.append(".*");
            Class<?> valueClazz = TypeUtil.getClass(getValueTypeOfMap(type));
            if (valueClazz == null || String.class.isAssignableFrom(valueClazz) || ClassUtil.isBasicType(valueClazz)) {
                beanPaths.add(startPath.substring(1));
                return;
            }

            scanBeanPath(valueClazz, startPath, beanPaths);
            return;
        }


        // 非bean，路径不再往下
        if (!StrUtil.isBlank(startPath)) {
            beanPaths.add(startPath.substring(1));
        }
    }

    private static Type[] getActualTypesOfMap(Type mapType) {
        if (mapType instanceof ParameterizedType) {
            return ((ParameterizedType) mapType).getActualTypeArguments();
        }
        return null;
    }

    private static Type getValueTypeOfMap(Type mapType) {
        Type[] actualTypesOfMap = getActualTypesOfMap(mapType);
        if (actualTypesOfMap == null) {
            return null;
        }

        return actualTypesOfMap[1];
    }

    private static Type getKeyTypeOfMap(Type mapType) {
        Type[] actualTypesOfMap = getActualTypesOfMap(mapType);
        if (actualTypesOfMap == null) {
            return null;
        }

        return actualTypesOfMap[0];
    }

    private static Type getActualTypeOfList(Type listType) {
        if (listType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) listType).getActualTypeArguments();
            if (actualTypeArguments == null || actualTypeArguments.length == 0) {
                return null;
            }
            return actualTypeArguments[0];
        }

        return null;
    }

    /**
     * 获取泛型类
     */
    private Class<?> getGenericType(Type type) {
        if (type instanceof ParameterizedType) {
            return (Class<?>) ((ParameterizedType) type).getRawType();
        } else {
            return (Class<?>) type;
        }
    }
}
